Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[YUNIKORN-2068] E2E Test for Preemption #705

Closed
wants to merge 27 commits into from

Conversation

rrajesh-cloudera
Copy link
Contributor

@rrajesh-cloudera rrajesh-cloudera commented Oct 26, 2023

Added a new test case to cover preemption scenarios on a specific node.

What type of PR is it?

  • - Bug Fix
  • - Improvement
  • - Feature
  • - Documentation
  • - Hot Fix
  • - Refactoring

What is the Jira issue?

https://issues.apache.org/jira/browse/YUNIKORN-2069

@codecov
Copy link

codecov bot commented Oct 27, 2023

Codecov Report

All modified and coverable lines are covered by tests ✅

Comparison is base (44d8c38) 71.65% compared to head (461cff1) 69.52%.
Report is 24 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##           master     #705      +/-   ##
==========================================
- Coverage   71.65%   69.52%   -2.14%     
==========================================
  Files          50       50              
  Lines        7953     7993      +40     
==========================================
- Hits         5699     5557     -142     
- Misses       2056     2248     +192     
+ Partials      198      188      -10     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@wilfred-s wilfred-s requested a review from manirajv06 October 30, 2023 07:55
@pbacsko pbacsko self-requested a review November 2, 2023 16:03
Copy link
Contributor

@pbacsko pbacsko left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rrajesh-cloudera the test case failed during the latest run:

2023-11-07T05:22:50.5444473Z �[0mPreemption �[0m�[1mVerify_preemption_on_specific_node�[0m
2023-11-07T05:22:50.5446062Z �[38;5;243m/home/runner/work/yunikorn-k8shim/yunikorn-k8shim/test/e2e/preemption/preemption_test.go:553�[0m
2023-11-07T05:22:50.5447933Z   �[1mSTEP:�[0m Create Two Queue High and Low Guaranteed Limit �[38;5;243m@ 11/07/23 05:22:50.544�[0m
2023-11-07T05:22:50.5455737Z   �[1mSTEP:�[0m Port-forward the scheduler pod �[38;5;243m@ 11/07/23 05:22:50.545�[0m
2023-11-07T05:22:50.5459264Z port-forward is already running  �[1mSTEP:�[0m Enabling new scheduling config �[38;5;243m@ 11/07/23 05:22:50.545�[0m
2023-11-07T05:22:53.5573043Z   �[1mSTEP:�[0m Schedule a number of small, Low priority pause tasks on Low Guaranteed queue (Enough to fill the node) �[38;5;243m@ 11/07/23 05:22:53.556�[0m
2023-11-07T05:22:53.5574921Z   �[1mSTEP:�[0m Deploy the sleep pod sleepjob1 to the development namespace �[38;5;243m@ 11/07/23 05:22:53.557�[0m
2023-11-07T05:23:53.7602458Z   �[38;5;9m[FAILED]�[0m in [It] - /home/runner/work/yunikorn-k8shim/yunikorn-k8shim/test/e2e/preemption/preemption_test.go:603 �[38;5;243m@ 11/07/23 05:23:53.759�[0m
2023-11-07T05:23:53.7604096Z   Unexpected error:
2023-11-07T05:23:53.7613907Z       <context.deadlineExceededError>: 
2023-11-07T05:23:53.7614589Z       context deadline exceeded
2023-11-07T05:23:53.7615095Z       {}

@pbacsko
Copy link
Contributor

pbacsko commented Nov 9, 2023

@rrajesh-cloudera you can avoid lintern problems by locally running "make lint".

Copy link
Contributor

@pbacsko pbacsko left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some comments, there's one major. Double check the output of Yunikorn. We don't want to trigger the DaemonSet-specific preemption logic.

}
}
Ω(sandbox1RunningPodsCnt).To(gomega.Equal(2), "One of the pods in root.sandbox1 should be preempted")
errNew := kClient.DeletePods(newNamespace.Name)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Already performed inside AfterEach()

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After each function is doing cleanup for a global namespace which is created for all the tests, In this test I am creating a new namespace and doing cleanup at the end of the test function to ensure there is no resources are available. I tried with the Global namespace and was facing some issue so trying to create inside the test script only and clean once execution is done.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you need a separate namespace for each test case, that should be done in BeforeEach/AfterEach. If something fails before this code, it will never be cleaned up.

Example:

ginkgo.BeforeEach(func() {
		kubeClient = k8s.KubeCtl{}
		gomega.Expect(kubeClient.SetClient()).To(gomega.BeNil())
		ns = "ns-" + common.RandSeq(10)
		ginkgo.By(fmt.Sprintf("Creating namespace: %s for admission controller tests", ns))
		var ns1, err1 = kubeClient.CreateNamespace(ns, nil)
		gomega.Ω(err1).NotTo(gomega.HaveOccurred())
		gomega.Ω(ns1.Status.Phase).To(gomega.Equal(v1.NamespaceActive))
	})

...

	ginkgo.AfterEach(func() {
		ginkgo.By("Tear down namespace: " + ns)
		err := kubeClient.TearDownNamespace(ns)
		gomega.Ω(err).NotTo(gomega.HaveOccurred())
		// call the healthCheck api to check scheduler health
		ginkgo.By("Check YuniKorn's health")
		checks, err2 := yunikorn.GetFailedHealthChecks()
		gomega.Ω(err2).ShouldNot(gomega.HaveOccurred())
		gomega.Ω(checks).Should(gomega.Equal(""), checks)
	})

6. This should trigger preemption on low-priority queue and remove or preempt task from low priority queue
7. Do cleanup once test is done either passed or failed
*/
time.Sleep(20 * time.Second)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we start with a 20 second sleep? Looks very arbitrary.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Cleanup for other tests is taking time and because of that the test which I added is failing intermittently for a few k8s versions. Added a sleep before the execution of test so it will trigger once the cleanup is done and resources are available to allocate.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's interesting. I think the reason is that tests are not using different namespaces. See my comment below, you need to create/destroy a namespace for each test. This is what solved many problems for the admission controller tests, they often interfered with each other, debugging was difficult. A new namespace for every test solved it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi Peter, Checked your comments and tried that approach but that required a lot of changes since we are using common namespace for all the tests so need to update each namespace as well. Also facing some issue with your approach.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does it need a lot of changes? That's what we're doing everywhere. Tests should be isolated by different namespaces. This 20 seconds sleep is only acceptable as a workaround. I'll try to see how difficult that is, but at least this must be addressed in a follow-up JIRA. This cannot stay as is.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, I can see what the problem is. I'll create a JIRA to separate the test cases. It's unacceptable to have all tests in a single namespace. They can easily interfere with each other.

https://issues.apache.org/jira/browse/YUNIKORN-2247

@@ -572,3 +656,11 @@ func createSandbox1SleepPodCofigs(cnt, time int) []k8s.SleepPodConfig {
}
return sandbox1Configs
}

func createSandbox1SleepPodCofigsWithStaticNode(cnt, time int) []k8s.SleepPodConfig {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"WithRequiredNode" or "WithNodeSelector" sounds better

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Acknowledged.

func createSandbox1SleepPodCofigsWithStaticNode(cnt, time int) []k8s.SleepPodConfig {
sandbox1Configs := make([]k8s.SleepPodConfig, 0, cnt)
for i := 0; i < cnt; i++ {
sandbox1Configs = append(sandbox1Configs, k8s.SleepPodConfig{Name: fmt.Sprintf("sleepjob%d", i+1), NS: devNew, Mem: sleepPodMemLimit2, Time: time, Optedout: k8s.Allow, Labels: map[string]string{"queue": "root.sandbox1"}, RequiredNode: nodeName})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This part is very suspicious. You're using the "RequiredNode" setting here, which will trigger the "simple" (aka. RequiredNode) preemption logic inside Yunikorn. That is not the generic preemption logic that Craig worked on.

Please check if the console log of Yunikorn contains this:

log.Log(log.SchedApplication).Info("Triggering preemption process for daemon set ask",
		zap.String("ds allocation key", ask.GetAllocationKey()))

If this is the case (which is VERY likely), changes are necessary.

A trivial solution is to enhance the RequiredNode field, it has to be a struct like:

type RequiredNode struct {
  Node      string
  DaemonSet bool
}

type SleepPodConfig struct {
...
	Mem          int64
	RequiredNode RequiredNode 
	Optedout     AllowPreemptOpted
}

If DaemonSet == false, then this code doesn't run:

owner := metav1.OwnerReference{APIVersion: "v1", Kind: constants.DaemonSetType, Name: "daemonset job", UID: "daemonset"}
owners = []metav1.OwnerReference{owner}

So this line effectively becomes:

sandbox1Configs = append(sandbox1Configs,
 k8s.SleepPodConfig{
    Name: fmt.Sprintf("sleepjob%d", i+1),
    NS: devNew, Mem: sleepPodMemLimit2,
    Time: time,
    Optedout: k8s.Allow,
    Labels: map[string]string{"queue": "root.sandbox1"},
    RequiredNode: RequiredNode{Node: nodeName, DeamonSet: false}
    }
)

The existing tests inside simple_preemptor_test.go are affected.

Copy link
Contributor

@pbacsko pbacsko Dec 5, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have you looked at this? Again, the tests should address YUNIKORN-2068. RequiredNodePreemptor does not use the predicates.

@rrajesh-cloudera
Copy link
Contributor Author

Hi @pbacsko , @craigcondit , @manirajv06 , @FrankYang0529 Please review the PR and provide your inputs.

@pbacsko
Copy link
Contributor

pbacsko commented Dec 21, 2023

Hi @pbacsko , @craigcondit , @manirajv06 , @FrankYang0529 Please review the PR and provide your inputs.

@rrajesh-cloudera as I mentioned in my comment (#705 (comment)) I don't think this test properly verifies what it is intended to. At this point I'm not even sure that we need an e2e test, because we're trying to replicate a scenario which causes (or used to cause) a race condition. A smoke test using MockScheduler might actually be a better solution here.

We can make a decision after everyone is back from vacation.

@pbacsko
Copy link
Contributor

pbacsko commented Dec 21, 2023

@rrajesh-cloudera please follow-up on my comment and check if the text "Triggering preemption process for daemon set ask" is present in the YK logs or not. I assume you can run the test locally in isolation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants